home *** CD-ROM | disk | FTP | other *** search
/ TeX 1995 July / TeX CD-ROM July 1995 (Disc 1)(Walnut Creek)(1995).ISO / macros / texinfo / info / infodoc.c < prev    next >
C/C++ Source or Header  |  1994-01-28  |  18KB  |  690 lines

  1. /* infohelp.c -- Functions which build documentation nodes. */
  2.  
  3. /* This file is part of GNU Info, a program for reading online documentation
  4.    stored in Info format.
  5.  
  6.    Copyright (C) 1993 Free Software Foundation, Inc.
  7.  
  8.    This program is free software; you can redistribute it and/or modify
  9.    it under the terms of the GNU General Public License as published by
  10.    the Free Software Foundation; either version 2, or (at your option)
  11.    any later version.
  12.  
  13.    This program is distributed in the hope that it will be useful,
  14.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  15.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16.    GNU General Public License for more details.
  17.  
  18.    You should have received a copy of the GNU General Public License
  19.    along with this program; if not, write to the Free Software
  20.    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  21.  
  22.    Written by Brian Fox (bfox@ai.mit.edu). */
  23.  
  24. #include "info.h"
  25.  
  26. /* **************************************************************** */
  27. /*                                    */
  28. /*              Info Help Windows                */
  29. /*                                    */
  30. /* **************************************************************** */
  31.  
  32. /* The name of the node used in the help window. */
  33. static char *info_help_nodename = "*Info Help*";
  34.  
  35. /* A node containing printed key bindings and their documentation. */
  36. static NODE *internal_info_help_node = (NODE *)NULL;
  37.  
  38. /* The static text which appears in the internal info help node. */
  39. static char *info_internal_help_text[] = {
  40.   "Basic Commands in Info Windows",
  41.   "******************************",
  42.   "",
  43.   "  h   Invoke the Info tutorial.",
  44.   "",
  45.   "Selecting other nodes:",
  46.   "----------------------",
  47.   "  n   Move to the \"next\" node of this node.",
  48.   "  p   Move to the \"previous\" node of this node.",
  49.   "  u   Move \"up\" from this node.",
  50.   "  m   Pick menu item specified by name.",
  51.   "      Picking a menu item causes another node to be selected.",
  52.   "  f   Follow a cross reference.  Reads name of reference.",
  53.   "  l   Move to the last node seen in this window.",
  54.   "  d   Move to the `directory' node.  Equivalent to `g(DIR)'.",
  55.   "",
  56.   "Moving within a node:",
  57.   "---------------------",
  58.   "  SPC Scroll forward a page.",
  59.   "  DEL Scroll backward a page.",
  60.   "  b   Go to the beginning of this node.",
  61.   "  e   Go to the end of this node.",
  62.   "",
  63.   "\"Advanced\" commands:",
  64.   "--------------------",
  65.   "  q   Quit Info.",
  66.   "  1   Pick first item in node's menu.",
  67.   "  2-9 Pick second ... ninth item in node's menu.",
  68.   "  0   Pick last item in node's menu.",
  69.   "  g   Move to node specified by name.",
  70.   "      You may include a filename as well, as in (FILENAME)NODENAME.",
  71.   "  s   Search through this Info file for a specified string,",
  72.   "      and select the node in which the next occurrence is found.",
  73.   (char *)NULL
  74. };
  75.  
  76. void
  77. dump_map_to_message_buffer (prefix, map)
  78.      char *prefix;
  79.      Keymap map;
  80. {
  81.   register int i;
  82.  
  83.   for (i = 0; i < 256; i++)
  84.     {
  85.       if (map[i].type == ISKMAP)
  86.     {
  87.       char *new_prefix, *keyname;
  88.  
  89.       keyname = pretty_keyname (i);
  90.       new_prefix = (char *)
  91.         xmalloc (3 + strlen (prefix) + strlen (keyname));
  92.       sprintf (new_prefix, "%s%s%s ", prefix, *prefix ? " " : "", keyname);
  93.  
  94.       dump_map_to_message_buffer (new_prefix, (Keymap)map[i].function);
  95.       free (new_prefix);
  96.     }
  97.       else if (map[i].function)
  98.     {
  99.       register int last;
  100.       char *doc, *name;
  101.  
  102.       doc = function_documentation (map[i].function);
  103.       name = function_name (map[i].function);
  104.  
  105.       if (!*doc)
  106.         continue;
  107.  
  108.       /* Find out if there is a series of identical functions, as in
  109.          ea_insert (). */
  110.       for (last = i + 1; last < 256; last++)
  111.         if ((map[last].type != ISFUNC) ||
  112.         (map[last].function != map[i].function))
  113.           break;
  114.  
  115.       if (last - 1 != i)
  116.         {
  117.           printf_to_message_buffer
  118.         ("%s%s .. ", prefix, pretty_keyname (i));
  119.           printf_to_message_buffer
  120.         ("%s%s\t", prefix, pretty_keyname (last - 1));
  121.           i = last - 1;
  122.         }
  123.       else
  124.         printf_to_message_buffer ("%s%s\t", prefix, pretty_keyname (i));
  125.  
  126. #if defined (NAMED_FUNCTIONS)
  127.       /* Print the name of the function, and some padding before the
  128.          documentation string is printed. */
  129.       {
  130.         int length_so_far;
  131.         int desired_doc_start = 40;    /* Must be multiple of 8. */
  132.  
  133.         printf_to_message_buffer ("(%s)", name);
  134.         length_so_far = message_buffer_length_this_line ();
  135.  
  136.         if ((desired_doc_start + strlen (doc)) >= the_screen->width)
  137.           printf_to_message_buffer ("\n     ");
  138.         else
  139.           {
  140.         while (length_so_far < desired_doc_start)
  141.           {
  142.             printf_to_message_buffer ("\t");
  143.             length_so_far += character_width ('\t', length_so_far);
  144.           }
  145.           }
  146.       }
  147. #endif /* NAMED_FUNCTIONS */
  148.       printf_to_message_buffer ("%s\n", doc);
  149.     }
  150.     }
  151. }
  152.  
  153. /* How to create internal_info_help_node. */
  154. static void
  155. create_internal_info_help_node ()
  156. {
  157.   register int i;
  158.  
  159.   initialize_message_buffer ();
  160.  
  161.   for (i = 0; info_internal_help_text[i]; i++)
  162.     printf_to_message_buffer ("%s\n", info_internal_help_text[i]);
  163.  
  164.   printf_to_message_buffer ("---------------------\n\n");
  165.   printf_to_message_buffer ("The current search path is:\n");
  166.   printf_to_message_buffer ("  \"%s\"\n", infopath);
  167.   printf_to_message_buffer ("---------------------\n\n");
  168.   printf_to_message_buffer ("Commands available in Info windows:\n\n");
  169.   dump_map_to_message_buffer ("", info_keymap);
  170.   printf_to_message_buffer ("---------------------\n\n");
  171.   printf_to_message_buffer ("Commands available in the echo area:\n\n");
  172.   dump_map_to_message_buffer ("", echo_area_keymap);
  173.  
  174.   {
  175.     char *message;
  176.  
  177.     message = replace_in_documentation
  178.       ("--- Use `\\[history-node]' or `\\[kill-node]' to exit ---\n");
  179.     printf_to_message_buffer ("%s", message);
  180.   }
  181.  
  182.   internal_info_help_node = message_buffer_to_node ();
  183.   add_gcable_pointer (internal_info_help_node->contents);
  184.   name_internal_node (internal_info_help_node, info_help_nodename);
  185.  
  186.   /* Even though this is an internal node, we don't want the window
  187.      system to treat it specially.  So we turn off the internalness
  188.      of it here. */
  189.   internal_info_help_node->flags &= ~N_IsInternal;
  190. }
  191.  
  192. /* Return a window which is the window showing help in this Info. */
  193. static WINDOW *
  194. info_find_or_create_help_window ()
  195. {
  196.   WINDOW *help_window;
  197.  
  198.   help_window = get_internal_info_window (info_help_nodename);
  199.  
  200.   /* If we couldn't find the help window, then make it. */
  201.   if (!help_window)
  202.     {
  203.       WINDOW *window, *eligible = (WINDOW *)NULL;
  204.       int max = 0;
  205.  
  206.       for (window = windows; window; window = window->next)
  207.     {
  208.       if (window->height > max)
  209.         {
  210.           max = window->height;
  211.           eligible = window;
  212.         }
  213.     }
  214.  
  215.       if (!eligible)
  216.     return ((WINDOW *)NULL);
  217.       else
  218.     {
  219.       /* Make a new node containing the help text.  Split the largest
  220.          window into 2 windows, and show the help text in that window. */
  221.       if (!internal_info_help_node)
  222.         create_internal_info_help_node ();
  223.  
  224.       if (eligible->height > 30)
  225.         {
  226.           active_window = eligible;
  227.           help_window = window_make_window (internal_info_help_node);
  228.         }
  229.       else
  230.         {
  231.           set_remembered_pagetop_and_point (active_window);
  232.           window_set_node_of_window
  233.         (active_window, internal_info_help_node);
  234.           help_window = active_window;
  235.         }
  236.  
  237.       remember_window_and_node (help_window, help_window->node);
  238.     }
  239.     }
  240.   return (help_window);
  241. }
  242.  
  243. /* Create or move to the help window. */
  244. DECLARE_INFO_COMMAND (info_get_help_window, "Display help message")
  245. {
  246.   WINDOW *help_window;
  247.  
  248.   help_window = info_find_or_create_help_window ();
  249.   if (help_window)
  250.     {
  251.       active_window = help_window;
  252.       active_window->flags |= W_UpdateWindow;
  253.     }
  254.   else
  255.     {
  256.       info_error (CANT_MAKE_HELP);
  257.     }
  258. }
  259.  
  260. /* Show the Info help node.  This means that the "info" file is installed
  261.    where it can easily be found on your system. */
  262. DECLARE_INFO_COMMAND (info_get_info_help_node, "Visit Info node `(info)Help'")
  263. {
  264.   NODE *node;
  265.   char *nodename;
  266.  
  267.   /* If there is a window on the screen showing the node "(info)Help" or
  268.      the node "(info)Help-Small-Screen", simply select that window. */
  269.   {
  270.     WINDOW *win;
  271.  
  272.     for (win = windows; win; win = win->next)
  273.       {
  274.     if (win->node && win->node->filename &&
  275.         (stricmp
  276.          (filename_non_directory (win->node->filename), "info") == 0) &&
  277.         ((strcmp (win->node->nodename, "Help") == 0) ||
  278.          (strcmp (win->node->nodename, "Help-Small-Screen") == 0)))
  279.       {
  280.         active_window = win;
  281.         return;
  282.       }
  283.       }
  284.   }
  285.  
  286.   /* If the current window is small, show the small screen help. */
  287.   if (active_window->height < 24)
  288.     nodename = "Help-Small-Screen";
  289.   else
  290.     nodename = "Help";
  291.  
  292.   /* Try to get the info file for Info. */
  293.   node = info_get_node ("Info", nodename);
  294.  
  295.   if (!node)
  296.     {
  297.       if (info_recent_file_error)
  298.     info_error (info_recent_file_error);
  299.       else
  300.     info_error (CANT_FILE_NODE, "Info", nodename);
  301.     }
  302.   else
  303.     {
  304.       /* If the current window is very large (greater than 45 lines),
  305.      then split it and show the help node in another window.
  306.      Otherwise, use the current window. */
  307.  
  308.       if (active_window->height > 45)
  309.     active_window = window_make_window (node);
  310.       else
  311.     {
  312.       set_remembered_pagetop_and_point (active_window);
  313.       window_set_node_of_window (active_window, node);
  314.     }
  315.  
  316.       remember_window_and_node (active_window, node);
  317.     }
  318. }
  319.  
  320. /* **************************************************************** */
  321. /*                                    */
  322. /*             Groveling Info Keymaps and Docs            */
  323. /*                                    */
  324. /* **************************************************************** */
  325.  
  326. /* Return the documentation associated with the Info command FUNCTION. */
  327. char *
  328. function_documentation (function)
  329.      VFunction *function;
  330. {
  331.   register int i;
  332.  
  333.   for (i = 0; function_doc_array[i].func; i++)
  334.     if (function == function_doc_array[i].func)
  335.       break;
  336.  
  337.   return (replace_in_documentation (function_doc_array[i].doc));
  338. }
  339.  
  340. #if defined (NAMED_FUNCTIONS)
  341. /* Return the user-visible name of the function associated with the
  342.    Info command FUNCTION. */
  343. char *
  344. function_name (function)
  345.  
  346.      VFunction *function;
  347. {
  348.   register int i;
  349.  
  350.   for (i = 0; function_doc_array[i].func; i++)
  351.     if (function == function_doc_array[i].func)
  352.       break;
  353.  
  354.   return (function_doc_array[i].func_name);
  355. }
  356.  
  357. /* Return a pointer to the function named NAME. */
  358. VFunction *
  359. named_function (name)
  360.      char *name;
  361. {
  362.   register int i;
  363.  
  364.   for (i = 0; function_doc_array[i].func; i++)
  365.     if (strcmp (function_doc_array[i].func_name, name) == 0)
  366.       break;
  367.  
  368.   return (function_doc_array[i].func);
  369. }
  370. #endif /* NAMED_FUNCTIONS */
  371.  
  372. /* Return the documentation associated with KEY in MAP. */
  373. char *
  374. key_documentation (key, map)
  375.      char key;
  376.      Keymap map;
  377. {
  378.   VFunction *function = map[key].function;
  379.  
  380.   if (function)
  381.     return (function_documentation (function));
  382.   else
  383.     return ((char *)NULL);
  384. }
  385.  
  386. DECLARE_INFO_COMMAND (describe_key, "Print documentation for KEY")
  387. {
  388.   char keyname[50];
  389.   int keyname_index = 0;
  390.   unsigned char keystroke;
  391.   char *rep;
  392.   Keymap map;
  393.  
  394.   keyname[0] = '\0';
  395.   map = window->keymap;
  396.  
  397.   while (1)
  398.     {
  399.       message_in_echo_area ("Describe key: %s", keyname);
  400.       keystroke = info_get_input_char ();
  401.       unmessage_in_echo_area ();
  402.  
  403.       if (Meta_p (keystroke) && (!ISO_Latin_p || key < 160))
  404.     {
  405.       if (map[ESC].type != ISKMAP)
  406.         {
  407.           window_message_in_echo_area
  408.         ("ESC %s is undefined.", pretty_keyname (UnMeta (keystroke)));
  409.           return;
  410.         }
  411.  
  412.       strcpy (keyname + keyname_index, "ESC ");
  413.       keyname_index = strlen (keyname);
  414.       keystroke = UnMeta (keystroke);
  415.       map = (Keymap)map[ESC].function;
  416.     }
  417.  
  418.       /* Add the printed representation of KEYSTROKE to our keyname. */
  419.       rep = pretty_keyname (keystroke);
  420.       strcpy (keyname + keyname_index, rep);
  421.       keyname_index = strlen (keyname);
  422.  
  423.       if (map[keystroke].function == (VFunction *)NULL)
  424.     {
  425.       message_in_echo_area ("%s is undefined.", keyname);
  426.       return;
  427.     }
  428.       else if (map[keystroke].type == ISKMAP)
  429.     {
  430.       map = (Keymap)map[keystroke].function;
  431.       strcat (keyname, " ");
  432.       keyname_index = strlen (keyname);
  433.       continue;
  434.     }
  435.       else
  436.     {
  437.       char *message, *fundoc, *funname = "";
  438.  
  439. #if defined (NAMED_FUNCTIONS)
  440.       funname = function_name (map[keystroke].function);
  441. #endif /* NAMED_FUNCTIONS */
  442.  
  443.       fundoc = function_documentation (map[keystroke].function);
  444.  
  445.       message = (char *)xmalloc
  446.         (10 + strlen (keyname) + strlen (fundoc) + strlen (funname));
  447.  
  448. #if defined (NAMED_FUNCTIONS)
  449.       sprintf (message, "%s (%s): %s.", keyname, funname, fundoc);
  450. #else
  451.       sprintf (message, "%s is defined to %s.", keyname, fundoc);
  452. #endif /* !NAMED_FUNCTIONS */
  453.  
  454.       window_message_in_echo_area ("%s", message);
  455.       free (message);
  456.       break;
  457.     }
  458.     }
  459. }
  460.  
  461. /* How to get the pretty printable name of a character. */
  462. static char rep_buffer[30];
  463.  
  464. char *
  465. pretty_keyname (key)
  466.      unsigned char key;
  467. {
  468.   char *rep;
  469.  
  470.   if (Meta_p (key))
  471.     {
  472.       char temp[20];
  473.  
  474.       rep = pretty_keyname (UnMeta (key));
  475.  
  476.       sprintf (temp, "ESC %s", rep);
  477.       strcpy (rep_buffer, temp);
  478.       rep = rep_buffer;
  479.     }
  480.   else if (Control_p (key))
  481.     {
  482.       switch (key)
  483.     {
  484.     case '\n': rep = "LFD"; break;
  485.     case '\t': rep = "TAB"; break;
  486.     case '\r': rep = "RET"; break;
  487.     case ESC:  rep = "ESC"; break;
  488.  
  489.     default:
  490.       sprintf (rep_buffer, "C-%c", UnControl (key));
  491.       rep = rep_buffer;
  492.     }
  493.     }
  494.   else
  495.     {
  496.       switch (key)
  497.     {
  498.     case ' ': rep = "SPC"; break;
  499.     case DEL: rep = "DEL"; break;
  500.     default:
  501.       rep_buffer[0] = key;
  502.       rep_buffer[1] = '\0';
  503.       rep = rep_buffer;
  504.     }
  505.     }
  506.   return (rep);
  507. }
  508.  
  509. /* Replace the names of functions with the key that invokes them. */
  510. static char *where_is (), *where_is_internal ();
  511.  
  512. char *
  513. replace_in_documentation (string)
  514.      char *string;
  515. {
  516.   register int i, start, next;
  517.   static char *result = (char *)NULL;
  518.  
  519.   maybe_free (result);
  520.   result = (char *)xmalloc (1 + strlen (string));
  521.  
  522.   i = next = start = 0;
  523.  
  524.   /* Skip to the beginning of a replaceable function. */
  525.   for (i = start; string[i]; i++)
  526.     {
  527.       /* Is this the start of a replaceable function name? */
  528.       if (string[i] == '\\' && string[i + 1] == '[')
  529.     {
  530.       char *fun_name, *rep;
  531.       VFunction *function;
  532.  
  533.       /* Copy in the old text. */
  534.       strncpy (result + next, string + start, i - start);
  535.       next += (i - start);
  536.       start = i + 2;
  537.  
  538.       /* Move to the end of the function name. */
  539.       for (i = start; string[i] && (string[i] != ']'); i++);
  540.  
  541.       fun_name = (char *)xmalloc (1 + i - start);
  542.       strncpy (fun_name, string + start, i - start);
  543.       fun_name[i - start] = '\0';
  544.  
  545.       /* Find a key which invokes this function in the info_keymap. */
  546.       function = named_function (fun_name);
  547.  
  548.       /* If the internal documentation string fails, there is a 
  549.          serious problem with the associated command's documentation.
  550.          We croak so that it can be fixed immediately. */
  551.       if (!function)
  552.         abort ();
  553.  
  554.       rep = where_is (info_keymap, function);
  555.       strcpy (result + next, rep);
  556.       next = strlen (result);
  557.  
  558.       start = i;
  559.       if (string[i])
  560.         start++;
  561.     }
  562.     }
  563.   strcpy (result + next, string + start);
  564.   return (result);
  565. }
  566.  
  567. /* Return a string of characters which could be typed from the keymap
  568.    MAP to invoke FUNCTION. */
  569. static char *where_is_rep = (char *)NULL;
  570. static int where_is_rep_index = 0;
  571. static int where_is_rep_size = 0;
  572.  
  573. static char *
  574. where_is (map, function)
  575.      Keymap map;
  576.      VFunction *function;
  577. {
  578.   char *rep;
  579.  
  580.   if (!where_is_rep_size)
  581.     where_is_rep = (char *)xmalloc (where_is_rep_size = 100);
  582.   where_is_rep_index = 0;
  583.  
  584.   rep = where_is_internal (map, function);
  585.  
  586.   /* If it couldn't be found, return "M-x Foo". */
  587.   if (!rep)
  588.     {
  589.       char *name;
  590.  
  591.       name = function_name (function);
  592.  
  593.       if (name)
  594.     sprintf (where_is_rep, "M-x %s", name);
  595.  
  596.       rep = where_is_rep;
  597.     }
  598.   return (rep);
  599. }
  600.  
  601. /* Return the printed rep of FUNCTION as found in MAP, or NULL. */
  602. static char *
  603. where_is_internal (map, function)
  604.      Keymap map;
  605.      VFunction *function;
  606. {
  607.   register int i;
  608.   
  609.   /* If the function is directly invokable in MAP, return the representation
  610.      of that keystroke. */
  611.   for (i = 0; i < 256; i++)
  612.     if ((map[i].type == ISFUNC) && map[i].function == function)
  613.       {
  614.     sprintf (where_is_rep + where_is_rep_index, "%s", pretty_keyname (i));
  615.     return (where_is_rep);
  616.       }
  617.  
  618.   /* Okay, search subsequent maps for this function. */
  619.   for (i = 0; i < 256; i++)
  620.     {
  621.       if (map[i].type == ISKMAP)
  622.     {
  623.       int saved_index = where_is_rep_index;
  624.       char *rep;
  625.  
  626.       sprintf (where_is_rep + where_is_rep_index, "%s ",
  627.            pretty_keyname (i));
  628.  
  629.       where_is_rep_index = strlen (where_is_rep);
  630.       rep = where_is_internal ((Keymap)map[i].function, function);
  631.  
  632.       if (rep)
  633.         return (where_is_rep);
  634.  
  635.       where_is_rep_index = saved_index;
  636.     }
  637.     }
  638.  
  639.   return ((char *)NULL);
  640. }
  641.  
  642. extern char *read_function_name ();
  643.  
  644. DECLARE_INFO_COMMAND (info_where_is,
  645.    "Show what to type to execute a given command")
  646. {
  647.   char *command_name;
  648.  
  649.   command_name = read_function_name ("Where is command: ", window);
  650.  
  651.   if (!command_name)
  652.     {
  653.       info_abort_key (active_window, count, key);
  654.       return;
  655.     }
  656.  
  657.   if (*command_name)
  658.     {
  659.       VFunction *function;
  660.  
  661.       function = named_function (command_name);
  662.  
  663.       if (function)
  664.     {
  665.       char *location;
  666.  
  667.       location = where_is (active_window->keymap, function);
  668.  
  669.       if (!location)
  670.         {
  671.           info_error ("`%s' is not on any keys", command_name);
  672.         }
  673.       else
  674.         {
  675.           if (strncmp (location, "M-x ", 4) == 0)
  676.         window_message_in_echo_area
  677.           ("%s can only be invoked via %s.", command_name, location);
  678.           else
  679.         window_message_in_echo_area
  680.           ("%s can be invoked via %s.", command_name, location);
  681.         }
  682.     }
  683.       else
  684.     info_error ("There is no function named `%s'", command_name);
  685.     }
  686.  
  687.   free (command_name);
  688. }
  689.     
  690.